package com.itcast.activiti.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * StringRedisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类, 采用Jackson2JsonRedisSerializer
 * RedisTemplate默认的系列化类是JdkSerializationRedisSerializer，用JdkSerializationRedisSerializer序列化的话，被序列化的对象必须实现Serializable接口。在存储内容时，除了属性的内容外还存了其它内容在里面，总长度长，且不容易阅读。
 * 我们要求是存储的数据可以方便查看，也方便反系列化，方便读取数据。
 * JacksonJsonRedisSerializer和GenericJackson2JsonRedisSerializer，两者都能系列化成json，但是后者会在json中加入@class属性，类的全路径包名，方便反系列化。前者如果存放了List则在反系列化的时候如果没指定TypeReference则会报错java.util.LinkedHashMap cannot be cast to 。
 * <p>
 * 全局缓存不失效，如果需要针对自动失效的，可以使用redisCacheConfigurationMap存放
 */
@Configuration
@EnableCaching
public class RedisCacheConfig {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(factory);
        // 设置key序列化类，否则key前面会多了一些乱码
        template.setKeySerializer(keySerializer());
        //设置value序列化
        template.setValueSerializer(valueSerializer());
        template.setHashKeySerializer(keySerializer());
        template.setHashValueSerializer(valueSerializer());
        template.afterPropertiesSet();
        template.setEnableTransactionSupport(true);
        return template;
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        return new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory),
                this.redisCacheConfiguration(300),
                this.redisCacheCustomConfiguration()
        );
    }

    private Map<String, RedisCacheConfiguration> redisCacheCustomConfiguration() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
//        redisCacheConfigurationMap.put("xxx", redisCacheConfiguration(7 * 24 * 60 * 60)); // 七天
        return redisCacheConfigurationMap;
    }


    private RedisCacheConfiguration redisCacheConfiguration(Integer second) {
        return RedisCacheConfiguration.defaultCacheConfig()
                // 如果是空值，不缓存
//                .disableCachingNullValues()
                // 设置key序列化器
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                //设置value序列化器
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer((valueSerializer())))
                .disableKeyPrefix()
                .entryTtl(Duration.ofSeconds(second));

    }

    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    private RedisSerializer<Object> valueSerializer() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
        return new GenericJackson2JsonRedisSerializer(objectMapper);
    }


}
